﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Threading.Tasks;
using System.IO;
using System.Diagnostics;

namespace EnglishStudy.Service
{
    /// <summary>
    /// This class exposes RESTful functionality using HTTPClient. 
    /// </summary>
    public class RestfulHttpClient : IRestfulHttpClient
    {
        public Uri BaseUri { get; private set; }
        private const string JsonMediaType = "application/json";
        private enum WebMethod
        {
            GET,
            POST
        }

        public RestfulHttpClient()
            : this((Uri)null)
        {
        }

        public void Init(Uri baseUri)
        {
            BaseUri = baseUri;
        }

        public void Init(String baseUri)
        {
            //Init(new Uri(baseUri));
        }

        /// <summary>
        /// <see cref="IRestfulHttpClient"/>
        /// </summary>


        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="baseUri">baseUri for httpclient.  A service path may be appended to this in the REST calls.</param>
        public RestfulHttpClient(Uri baseUri)
        {
            BaseUri = baseUri;
        }

        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="baseUri">baseUri for httpclient.  A service path may be appended to this in the REST calls.</param>
        public RestfulHttpClient(String baseUri)
            : this(new Uri(baseUri))
        {
        }

        /// <summary>
        /// <see cref="IRestfulHttpClient"/>
        /// </summary>
        /*
        private HttpWebRequest CreateHttpWebRequest(Uri baseUri)
        {

            return httpWebRequest;
        }
         */

        /// <summary>
        /// <see cref="IRestfulHttpClient"/>
        /// </summary>
        public async Task<IHttpResponseMessage> GetAsync(string servicePath, IDictionary<string, string> parms, string acceptMediaType = JsonMediaType)
        {
            HttpWebResponse httpResponseMessage;

            if (parms == null)
            {
                httpResponseMessage = await SendAsync(WebMethod.GET, servicePath, null);
            }
            else
            {
                string fullRequestUrl = servicePath;
                if (servicePath.IndexOf('?') == -1)
                {
                    fullRequestUrl += "?";
                }
                else
                {
                    fullRequestUrl += "&";
                }

                fullRequestUrl += GetQueryString(parms);
                httpResponseMessage = await SendAsync(WebMethod.GET, fullRequestUrl, null);
            }

            return new HttpResponseMessageWrapper(httpResponseMessage);
        }

        /// <summary>
        /// <see cref="IRestfulHttpClient"/>
        /// </summary>
        public async Task<IHttpResponseMessage> PostFormUrlEncodedAsync(string servicePath, IDictionary<string, string> parms, string acceptMediaType = JsonMediaType)
        {
            //var httpContent = new FormUrlEncodedContent(parms);

            HttpWebResponse responseMessage = await SendAsync(WebMethod.POST, servicePath, parms);

            return new HttpResponseMessageWrapper(responseMessage);
        }

        public async Task<IHttpResponseMessage> PostFormUrlEncodedAsync(string userName,string etoken,string servicePath, IDictionary<string, string> parms, string acceptMediaType = JsonMediaType)
        {
            //var httpContent = new FormUrlEncodedContent(parms);

            //HttpWebResponse responseMessage = await SendAsync(WebMethod.POST, servicePath, parms);
            HttpWebResponse responseMessage = await SendAsync(WebMethod.POST, servicePath, parms,userName,etoken);
            return new HttpResponseMessageWrapper(responseMessage);
        }

        public async Task<IHttpResponseMessage> PostFormUrlEncodedAsyncWithUser(string userName ,string etoken,string servicePath, IDictionary<string, string> parms, string acceptMediaType = JsonMediaType)
        {
            //var httpContent = new FormUrlEncodedContent(parms);

            HttpWebResponse responseMessage = await SendAsync(WebMethod.GET, servicePath, parms,userName,etoken);

            return new HttpResponseMessageWrapper(responseMessage);
        }

        private async Task<HttpWebResponse> SendAsync(WebMethod httpMethod, string servicePath, IDictionary<string, string> parms)
        {
            // Get Method
            switch (httpMethod)
            {
                case WebMethod.GET:
                    {
                        string url = String.Concat(BaseUri, servicePath).Trim();
                        Debug.WriteLine(url);
                        var httpWebRequest = WebRequest.Create(new Uri(url, UriKind.Absolute)) as HttpWebRequest;
                        httpWebRequest.Method = "GET";
                        httpWebRequest.Headers[HttpRequestHeader.CacheControl] = "no-cache";
                        httpWebRequest.Headers[HttpRequestHeader.Pragma] = "no-cache";

                        httpWebRequest.AllowReadStreamBuffering = true;

                        httpWebRequest.Headers[HttpRequestHeader.IfModifiedSince] = DateTime.Now.ToString();
                        var factory = new TaskFactory();
                        var task = factory.FromAsync<WebResponse>(httpWebRequest.BeginGetResponse, httpWebRequest.EndGetResponse, null);
                        try
                        {

                            var response = await task;
                            return (HttpWebResponse)response;
                        }
                        catch (Exception ex)
                        {
                            var we = ex.InnerException as WebException;
                            if (we != null)
                            {
                                var resp = we.Response as HttpWebResponse;
                                return resp;
                            }
                            throw;
                        }
                    }
                case WebMethod.POST:
                    {
                        var url = HttpUtility.UrlDecode(string.Concat(BaseUri, servicePath));
                        var httpWebRequest = (HttpWebRequest)WebRequest.Create(url);
                        httpWebRequest.ContentType = "application/x-www-form-urlencoded";
                        httpWebRequest.Method = "POST";
                        var requestStr = new StringBuilder();
                        foreach (string key in parms.Keys)
                        {
                                requestStr.Append(key + "=" + HttpUtility.UrlEncode(parms[key]) + "&");
                        }
                        requestStr.Length = requestStr.Length - 1;
                        var factory = new TaskFactory();
                        var task = factory.FromAsync<Stream>(httpWebRequest.BeginGetRequestStream, httpWebRequest.EndGetRequestStream, null);
                        try
                        {
                            Stream postStream = await task;
                            // Convert the string into a byte array. 
                            byte[] byteArray = Encoding.UTF8.GetBytes(requestStr.ToString());

                            // Write to the request stream.
                            postStream.Write(byteArray, 0, requestStr.Length);
                            postStream.Close();


                            var factory1 = new TaskFactory();
                            var task1 = factory1.FromAsync<WebResponse>(httpWebRequest.BeginGetResponse, httpWebRequest.EndGetResponse, null);
                            var response = await task1;
                            var m = (HttpWebResponse)response;


                            return m;
                        }
                        catch (WebException ex)
                        {
                            var we = ex.InnerException as WebException;
                            if (we != null)
                            {
                                var resp = we.Response as HttpWebResponse;
                                return resp;
                            }
                            throw;
                        }
                    }
            }
            return null;
        }

        private async Task<HttpWebResponse> SendAsync(WebMethod httpMethod, string servicePath, IDictionary<string, string> parms,string userName, string etoken)
        {
            // Get Method
            switch (httpMethod)
            {
                case WebMethod.GET:
                    {
                        string url = String.Concat(BaseUri, servicePath).Trim();
                        Debug.WriteLine(url);
                        var httpWebRequest = WebRequest.Create(new Uri(url, UriKind.Absolute)) as HttpWebRequest;
                        httpWebRequest.Method = "GET";
                        httpWebRequest.Headers[HttpRequestHeader.CacheControl] = "no-cache";
                        httpWebRequest.Headers[HttpRequestHeader.Pragma] = "no-cache";
                        httpWebRequest.Headers["Username"] = userName;
                        httpWebRequest.Headers["Etoken"] = etoken;
                        httpWebRequest.AllowReadStreamBuffering = true;

                        httpWebRequest.Headers[HttpRequestHeader.IfModifiedSince] = DateTime.Now.ToString();
                        var factory = new TaskFactory();
                        var task = factory.FromAsync<WebResponse>(httpWebRequest.BeginGetResponse, httpWebRequest.EndGetResponse, null);
                        try
                        {

                            var response = await task;
                            return (HttpWebResponse)response;
                        }
                        catch (Exception ex)
                        {
                            var we = ex.InnerException as WebException;
                            if (we != null)
                            {
                                var resp = we.Response as HttpWebResponse;
                                return resp;
                            }
                            throw;
                        }
                    }
                case WebMethod.POST:
                    {
                        var url = HttpUtility.UrlDecode(string.Concat(BaseUri, servicePath));
                        var httpWebRequest = (HttpWebRequest)WebRequest.Create(url);
                        httpWebRequest.ContentType = "application/x-www-form-urlencoded";
                        httpWebRequest.Method = "POST";
                        httpWebRequest.Headers["Username"] = userName;
                        httpWebRequest.Headers["Etoken"] = etoken;
                        var requestStr = new StringBuilder();
                        foreach (string key in parms.Keys)
                        {
                            requestStr.Append(key + "=" + HttpUtility.UrlEncode(parms[key]) + "&");
                        }
                        requestStr.Length = requestStr.Length - 1;
                        var factory = new TaskFactory();
                        var task = factory.FromAsync<Stream>(httpWebRequest.BeginGetRequestStream, httpWebRequest.EndGetRequestStream, null);
                        try
                        {
                            Stream postStream = await task;
                            // Convert the string into a byte array. 
                            byte[] byteArray = Encoding.UTF8.GetBytes(requestStr.ToString());

                            // Write to the request stream.
                            postStream.Write(byteArray, 0, requestStr.Length);
                            postStream.Close();


                            var factory1 = new TaskFactory();
                            var task1 = factory1.FromAsync<WebResponse>(httpWebRequest.BeginGetResponse, httpWebRequest.EndGetResponse, null);
                            var response = await task1;
                            var m = (HttpWebResponse)response;


                            return m;
                        }
                        catch (WebException ex)
                        {
                            var we = ex.InnerException as WebException;
                            if (we != null)
                            {
                                var resp = we.Response as HttpWebResponse;
                                return resp;
                            }
                            throw;
                        }
                    }
            }
            return null;
        }

        private static string GetQueryString(IDictionary<string, string> query)
        {
            var queryString = query.Aggregate("", (current, p) => current + (Uri.EscapeDataString(p.Key) + "=" + (p.Value == null ? "" : Uri.EscapeDataString(p.Value)) + "&"));
            return queryString.Trim('&');
        }
    }
}
